/**
 * 
 */
package co.edu.javeriana.ia.ga.fittness;

//import java.util.List;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.jgap.IChromosome;

import co.edu.javeriana.ia.ga.Constants;
import co.edu.javeriana.ia.ga.Descriptor;
import co.edu.javeriana.ia.ga.PrintUtil;
import co.edu.javeriana.ia.ga.RuleAllele;
import co.edu.javeriana.ia.ga.Secuence;

/**
 * <p>
 * Funcion de evaluacion de chromosomas
 * </p>
 * 
 * @author <a href=f.triana@javeriana.edu.co">Alejandro Triana</a>
 * @version 1.0
 */
public class GenClassificationFittness extends FitnessFunction {

	private static final int CORRECT_NUMBER = 1000;

	private static final long serialVersionUID = 1L;

	private List<Secuence> sequence;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.FitnessFunction#evaluate(org.jgap.IChromosome)
	 */
	public GenClassificationFittness(List<Secuence> sequence) {
		super();
		this.sequence = sequence;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.FitnessFunction#evaluate(org.jgap.IChromosome)
	 */
	@Override
	protected double evaluate(IChromosome chr) {

		double[] results;
		double[] totalResults = { 0, 0, 0, 0 };
		double eval = 0;

		for (Secuence s : this.sequence) {

			results = verifySecuenceClassification(chr, s);

			totalResults[0] += results[0];
			totalResults[1] += results[1];
			totalResults[2] += results[2];
			totalResults[3] += results[3];

		}

		eval += calculateWFunction(totalResults);

		return eval;
	}

	/**
	 * <p>
	 * Evalua la funcion para un cromosoma individual
	 * </p>
	 */
	public double evaluateTest(IChromosome chr, boolean print) {

		double[] results;
		double[] totalResults = { 0, 0, 0, 0 };
		double eval = 0;

		for (Secuence s : this.sequence) {

			results = verifySecuenceClassification(chr, s);

			totalResults[0] += results[0];
			totalResults[1] += results[1];
			totalResults[2] += results[2];
			totalResults[3] += results[3];

			// eval += calculateWFunction(results);
		}
		eval = calculateWFunction(totalResults);
		
		if (print) {
			PrintUtil.printFitnessResult(chr, this.sequence.size(), totalResults, eval);
		}
		
		return eval;
	}

	/**
	 * <p>
	 * Permite calcular el valor funcion de fitness
	 * </p>
	 */
	private double calculateWFunction(double[] resultados) {
		return (CORRECT_NUMBER * resultados[1])
				+ (resultados[0] / Constants.MAX_NUM_GENES)
				- (resultados[2] / Constants.MAX_NUM_GENES)
				+ (resultados[3] / (Constants.MAX_NUM_DESCRIPTORS * Constants.MAX_NUM_GENES));
		// return resultados[1];
	}

	/**
	 * <p>
	 * Permite verificar la validez de una secuencia
	 * </p>
	 */
	private double[] verifySecuenceClassification(IChromosome chr, Secuence s) {

		Gene[] genes = chr.getGenes();
		double[] results = { 0, 0, 0, 0 }; // noclasificados, correctos,
											// incorrectos, match_descriptores

		for (Gene g : genes) {

			RuleAllele allele = (RuleAllele) g.getAllele();

			// Se realiza el paso a un arreglo para asegurar la comparacion
			// Los descriptores estan ordenados de acuerdo a su posicion natural
			// implementado por el metodo compare
			Map<String, Descriptor> secuenceDescriptors = s.getDescriptors();
			Map<String, Descriptor> ruleDescriptors = allele.getCondition();

			Set<Entry<String, Descriptor>> setRuleDescriptors = ruleDescriptors
					.entrySet();
			int validCondition = 0;

			boolean isEqualGenome = allele.getAction().getInternalId() == s
					.getGenome().getInternalId();

			for (Entry<String, Descriptor> entryRuleDescriptor : setRuleDescriptors) {

				Descriptor itemRuleDescriptor = entryRuleDescriptor.getValue();
				Descriptor itemSecDescriptor = secuenceDescriptors
						.get(itemRuleDescriptor.getId());

				if (itemRuleDescriptor.getFrecuency() == itemSecDescriptor
						.getFrecuency()) {

					if (isEqualGenome) {
						entryRuleDescriptor.getValue().setMatch(true);
					}

					validCondition += 1;
				}
			}

			// Todas las clasificaciones fueron correctas
			if ((validCondition >= setRuleDescriptors.size() - 3)
					&& isEqualGenome) {
				results[1]++;
			}

			// Se hizo una clasificacion incorrecta
			if ((validCondition >= setRuleDescriptors.size() - 3)
					&& !isEqualGenome) {
				results[2]++;
			}

			// No se hizo la clasificacion
			if ((validCondition < setRuleDescriptors.size() - 3)
					&& isEqualGenome) {
				// results[0] += ((double)validCondition /
				// (double)setRuleDescriptors.size());
				results[0]++;
			}
			results[3] += validCondition;
		}
		return results;
	}

}
